tree grammar Translator;

options{
	tokenVocab=Calculator;
	ASTLabelType=CommonTree;
	output=template;
}

@header{
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
}

@members{
private int varSeq=1; //temporary variable sequence
private List<String> reasons = new ArrayList<String>(0);

public String getNewTempVar(){
	return "_var" + "_" + varSeq++;
}

public void reportLogicError(int line,int column,String reason){
	reasons.add("At line:" + line + " Column:" + column + " " + reason);
}

public List<String> getErrors(){
	return reasons;
}

//variable
private Map<String,Boolean> memory = new HashMap<String,Boolean>();

}

root	
scope{
List<String> statementList ;
}
@init{
$root::statementList = new ArrayList<String>();
}
	:	(a=statement{$root::statementList.add($a.st.toString());})+
	->	createUnit(statementList={$root::statementList})
	;

statement
scope{
List<String> statementList ;
boolean defined;
}
@init{
$statement::statementList = new ArrayList<String>();
}
	:	^(EQ ID expr)
	{
		$statement::defined = memory.get($ID.text) != null?memory.get($ID.text):Boolean.FALSE;
		memory.put($ID.text,Boolean.TRUE);
	}
	->	createAssign(left={$ID.text},rightvar={$expr.expr_var},right={$expr.st},defined={$statement::defined}) 
	|	^(EQ ID atom)
	{
		$statement::defined = memory.get($ID.text) != null?memory.get($ID.text):Boolean.FALSE;
		memory.put($ID.text,Boolean.TRUE);
	}
	->	createAssign(left={$ID.text},rightvar={$atom.text},defined={$statement::defined})
	|	^(ECHO node)
	->	createEcho(nodevar={$node.node_var},nodecode={$node.st})
	|	^(ECHO STR)
	->	createEcho(nodevar={$STR})
	|	^(LOOP a=ID b=node c=comp (d=statement{$statement::statementList.add($d.st.toString());})+)
	{
		$statement::defined = memory.get($ID.text) != null?memory.get($ID.text):Boolean.FALSE;
	}
	->	createLoop(repeatvar={$a.text},replacevar={$b.node_var},replacecode={$b.st},compcode={$c.st},compres={$c.comp_var},block={$statement::statementList},defined={$statement::defined})
	;

expr returns [String expr_var]
scope{
List node_vars ; 
List node_codes;
}
@init{
$expr_var = getNewTempVar();
$expr::node_vars = new ArrayList();
$expr::node_codes = new ArrayList();
}
	:	^(ADD (node{$expr::node_vars.add($node.node_var);$expr::node_codes.add($node.st);})+)
	->	createAdd(var={$expr_var},itemVars={$expr::node_vars},addItems={$expr::node_codes})  
	|	^(MUL (node{$expr::node_vars.add($node.node_var);$expr::node_codes.add($node.st);})+)
	->	createMul(var={$expr_var},itemVars={$expr::node_vars},mulItems={$expr::node_codes})
	|	^(SUB (node{$expr::node_vars.add($node.node_var);$expr::node_codes.add($node.st);})+)
	{
		if($expr::node_vars.size() > 2){
			reportLogicError($SUB.token.getLine(), $SUB.token.getCharPositionInLine() , "Substract operation only two nodes allowd!"); 
		}
	}
	->	createSub(var={$expr_var},itemVars={$expr::node_vars},subItems={$expr::node_codes})
	|	^(DIV (node{$expr::node_vars.add($node.node_var);$expr::node_codes.add($node.st);})+)
	{
		if($expr::node_vars.size() > 2){
			reportLogicError($DIV.token.getLine(), $DIV.token.getCharPositionInLine() , "Divide operation only two nodes allowd!"); 
		}
	}
	->	createDiv(var={$expr_var},itemVars={$expr::node_vars},divItems={$expr::node_codes})  
	; 
comp	returns [String comp_var]
@init{
$comp_var = getNewTempVar();
}
	:	^(a=COMP b=node c=node)
	->	createComp(compres={$comp_var},oper={$a.text},leftvar={$b.node_var},leftcode={$b.st},rightvar={$c.node_var},rightcode={$c.st})
	;	
node returns [String node_var]	
@init{
$node_var = getNewTempVar(); 
}
	:	expr 
	->	createExpr(left={$node_var},rightvar={$expr.expr_var},right={$expr.st})
	|	atom
	->	createAtom(left={$node_var},right={$atom.text})
	; 
	
atom	  
	:	NUM  
	{
		try{
			Integer.parseInt($NUM.text);
		}catch(NumberFormatException e){
			reportLogicError($NUM.token.getLine(), $NUM.token.getCharPositionInLine() , "Illegal Number Format!");
		}
	}
	-> 	createNumber(text={$NUM.text})
	| 	ID	
	{
		if(memory.get($ID.text) == null){
			reportLogicError($ID.token.getLine(), $ID.token.getCharPositionInLine() , $ID.text + " Identifier Undefined!");
		}
	}
	-> 	createID(text={$ID.text})
	;